#include "connection.h"
#include "waitingroom.h"
#include "board.h"

Connection::Connection() {
	memset(boardArray, 0, sizeof(boardArray));
	memset(rm, 0, sizeof(rm));
}

bool Connection::init(char* username, char* ip, int port) {
    struct sockaddr_in serv_addr;
	CODE cmd;
	connected = true;
	inRoom  = -1;

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		connected = false;
		return connected;
	}

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	serv_addr.sin_addr.s_addr = inet_addr(ip);

	if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
	{
		connected = false;
		return connected;
	}

    int len = strlen(username) + 1;
	cmd = USERNAME;
	write(sockfd, &cmd, sizeof(CODE));
	write(sockfd, &len, sizeof(int));
	write(sockfd, username, len);
	fsync(sockfd);

    return connected;
}

void Connection::startReading() {
	pthread_create(&thread, NULL, parseInput, this);
}

void Connection::addWaitingRoom(WaitingRoom* w) {
    roomList = w;
}

void *parseInput(void* param)
{
	Connection* server = (Connection*) param;
	CODE cmd, cmd2;
	int sockfd = server->sockfd;

	while(server->connected)
	{
		read(sockfd, &cmd, sizeof(CODE));

		if(cmd & COMMAND)
		{
            switch(cmd)
            {
			case SERVERFULL:
				printf("Server is full.\n");
				server->connected = 0;
				close(sockfd);
				exit(0);
				break;
			case ACCEPTED:
				//break;
			case ADMIN:
				read(sockfd, &cmd, sizeof(CODE));
				break;
			case DENIED:
				break;
			case USERNAME:
				read(sockfd, &cmd, sizeof(int));
				server->connected = cmd;
				break;
			case KICK:
				read(sockfd, &cmd, sizeof(int));
				printf("Server Kicked you out of room %d.\n", cmd);
				server->inRoom = -1;
				break;
			case JOIN:
				read(sockfd, &cmd, sizeof(int));
				read(sockfd, &cmd2, sizeof(int));
				printf("Your room has a new member %s.\n", server->getPlayer(cmd2)->name);
				break;
			case LEAVE:
				read(sockfd, &cmd, sizeof(int));
				read(sockfd, &cmd, sizeof(int));
				printf("%s has left the room.\n",  server->getPlayer(cmd)->name);
				break;
			case ROOM:
				cmd = server->readRoom();

				//displayRoomMembers(cmd);
				//hastMoves(cmd);
				break;

			case PLAYER:
				server->readPlayer();
				break;
			case WINNER:
				{
				int winningPlayer, winningRoom, xoro;

				read(sockfd, &winningRoom, sizeof(winningRoom));
				read(sockfd, &winningPlayer, sizeof(winningPlayer));
				read(sockfd, &xoro, sizeof(xoro));

				printf("Winning Room: %d\n", winningRoom);
				if(winningPlayer == CATSGAME)
					printf("Cat's Game\n");
				else if(winningPlayer == server->connected)
					printf("You WIN!\n");
				else
					printf("You LOSE.\n");
				}
				break;
			default:
				printf("Server sent unknown command %d.\n", cmd);
				break;
			}
		}
		else
		{
			int roomNum = 0;
			read(sockfd, &roomNum, sizeof(roomNum));
			/* printf("Received command %d for room %d\n", cmd, roomNum); */

			server->getBoard(roomNum)->move(cmd);
			//displayBoard();
			/* Movement Command recieved */
			Room* in = server->rm[server->inRoom];

			in->nextPlayer = NEXT(in, in->nextPlayer);
		}
	}
	return param;
}

Connection::~Connection() {
	if(connected)
		close(sockfd);

	for(int i = 0; i < MAXMEMBERS; i++) {
		delete rm[i];
	}
	for(int i = 0; i < MAXMEMBERS; i++) {
		delete p[i];
	}
}

void Connection::join(int i)
{
	int cmd = JOIN;
	write(sockfd, &cmd, sizeof(CODE));
	cmd = i;
	write(sockfd, &cmd, sizeof(int));
	fsync(sockfd);
}

void Connection::leave(int i)
{
	int cmd = LEAVE;
	write(sockfd, &cmd, sizeof(CODE));
	cmd = i;
	write(sockfd, &cmd, sizeof(int));
	fsync(sockfd);
}

Player* Connection::getPlayer(int id) {
	int i;
	for(i = 0; i < MAXMEMBERS; i++)
		if(p[i] != NULL && p[i]->ID == id)
			return p[i];
	return NULL;
}
board* Connection::getBoard(int id) {
	int i;
	for(i = 0; i < MAXBOARDS; i++)
		if(boardArray[i] != NULL && boardArray[i]->getRoomID() == id)
			return boardArray[i];
	return NULL;
}
/*
void Connection::displayRoomMembers(int rmid) {
	int j;
	Room* in = rm[rmid];
	printf("\n%s Members:\n", in->name);
	for(j = 0; j < in->maxForRoom; j++)
		if(in->players[j] != 0)
			printf("\tMember %d: %s\n", j+1, getPlayer(in->players[j])->name);
	puts("----------------------");
}
*/
void Connection::sendMove(CODE cmd, int room)
{
	printf("Sending command: %d for room %d\n", cmd, room);
	write(sockfd, &cmd, sizeof(cmd));
	write(sockfd, &room, sizeof(room));
}

int Connection::readRoom(void)
{
	int id, len;

	read(sockfd, &id, sizeof(int));
	if(rm[id] == NULL) {
		rm[id] = new Room();
		rm[id]->players = new int[MAXMEMBERS];
		memset(rm[id], 0, sizeof(Room));
		//memset(rm[id]->players, 0, MAXMEMBERS*sizeof(int));
	} else {
		delete rm[id]->name;
		memset(rm[id]->players, 0, MAXMEMBERS*sizeof(int));
	}
	rm[id]->ID = id;

	memset(rm[id]->moves, 0, sizeof(rm[id]->moves));

	read(sockfd, &len, sizeof(int));
	rm[id]->name = new char[len];
	read(sockfd, rm[id]->name, len);
	read(sockfd, &rm[id]->gamesPlayed, sizeof(int));
	read(sockfd, &rm[id]->maxForRoom, sizeof(int));
	read(sockfd, rm[id]->players, rm[id]->maxForRoom*sizeof(int));
	read(sockfd, &rm[id]->numberInRoom, sizeof(int));
	read(sockfd, &rm[id]->playerX, sizeof(int));
	read(sockfd, &rm[id]->playerO, sizeof(int));
	read(sockfd, &rm[id]->admin, sizeof(int));
	read(sockfd, rm[id]->moves, sizeof(rm[id]->moves));

	return id;
}

int Connection::readPlayer(void)
{
	int id, len, i;
	Player* ply;

	read(sockfd, &id, sizeof(int));
	if((ply = getPlayer(id)) == NULL) {
		for(i = 0; i < MAXMEMBERS; i++)
			if(p[i] == NULL) {
				p[i] = new Player();
				memset(p[i], 0, sizeof(Player));
				ply = p[i];
				break;
			}
	}
	else
		delete ply->name;
	ply->ID = id;

	read(sockfd, &len, sizeof(int));
	ply->name = new char[len];
	read(sockfd, ply->name, len);
	read(sockfd, &ply->gamesPlayed, sizeof(int));
	read(sockfd, &ply->wins, sizeof(int));
	read(sockfd, &ply->losses, sizeof(int));

	return id;
}

void Connection::addBoard(board* br) {
	for(int i = 0; i < MAXBOARDS; i++)
		if(boardArray[i] != NULL) {
			boardArray[i] = br;
			break;
		}
}

void Connection::removeBoard(board* br) {
	for(int i = 0; i < MAXBOARDS; i++)
		if(boardArray[i] == br)
			boardArray[i] = NULL;
}
/*
void Connection::hastMoves(int r) {
	int* serverMoves = rm[r]->moves;
	int i;
	for(i = 0; i < MOVES_IN_A_GAME; i++) {
		if(serverMoves[i] == 0) continue;
		storeMove(serverMoves[i]);
	}
}
*/
bool Connection::checkIfInRoom(int rmid)
{
	for(int i = 0; i < MAXBOARDS; i++)
		if(boardArray[i] != NULL && boardArray[i]->getRoomID() == rmid)
				return true;
	return false;
}
/*
int Connection::isInRange(int x, int lower, int upper)
{
	return x >= lower && x <= upper;
}

int Connection::isDirection(CODE c)
{
	return isInRange(c, CL_SQ_MIN, CL_SQ_MAX);
}
*
int Connection::getPlayerRepr(int sq)
{
	int move;
	if (!checkIfInRoom())
	{
		fprintf(stderr, "Not in a room\n");
		return -2;
	}/*
	else if (!isDirection(sq))
	{
		fprintf(stderr, "Invalid square number: %d\n", sq);
		return -1;
	}*
	move = rm[inRoom]->moves[sq-1];
	if (move & X) return 'X';
	else if (move & O) return 'O';
	else return '0' + sq;
}
/*
void Connection::displayBoard(void)
{
	Room* in = rm[server.inRoom];

	printf
	(
		"\n"
		CHAR_ROW
		BORDER_ROW
		CHAR_ROW
		BORDER_ROW
		CHAR_ROW,

		GPR(CL_UL), GPR(CL_U), GPR(CL_UR),
		GPR(CL_L ), GPR(CL_C), GPR(CL_R ),
		GPR(CL_DL), GPR(CL_D), GPR(CL_DR)
	);

	if (in->nextPlayer == connected)
		puts("-- Your turn --");
	else
		printf("-- %c's Turn --\n", in->nextPlayer == in->playerX ? 'X' : 'O');
}

void Connection::storeMove(int mv)
{
	int sq;

	switch (mv & ~(COMMAND | X | O))
	{
	case UPPERLEFT:
		sq = CL_UL;
		break;
	case UPPER:
		sq = CL_U;
		break;
	case UPPERRIGHT:
		sq = CL_UR;
		break;
	case LEFT:
		sq = CL_L;
		break;
	case CENTER:
		sq = CL_C;
		break;
	case RIGHT:
		sq = CL_R;
		break;
	case LOWERLEFT:
		sq = CL_DL;
		break;
	case LOWER:
		sq = CL_D;
		break;
	case LOWERRIGHT:
		sq = CL_DR;
		break;
	default:
		fprintf(stderr, "Unknown move: %d\n", mv);
		return;
	}

	rm[inRoom]->moves[sq-1] = mv;
}
*/
